<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <title>WebSocket Chat</title>
    <style>
        /* 颜色搭配 */
        body {
            background-color: #f9f9f9;
            color: #333;
            font-family: Verdana, sans-serif;
        }

        .container {
            width: 80%;
            margin: 20px auto;
            padding: 20px;
            border: 1px solid #ccc;
            border-radius: 5px;
            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
        }

        .container > h1 {
            text-align: center;
            margin-bottom: 20px;
        }

        .top {
            display: flex;
            justify-content: space-between;
            margin-bottom: 20px;
        }

        #messages, #privateMessages, #users, #rank, #historyMessages {
            border: 1px solid #ccc;
            padding: 10px;
            height: 300px;
            overflow-y: scroll;
            margin-bottom: 10px;
            text-align: center;
        }
        #privateMessages {
            display: none;
        }
        #historyMessages{
            display: none;
        }

        #messages::-webkit-scrollbar, #privateMessages::-webkit-scrollbar, #users::-webkit-scrollbar, #rank::-webkit-scrollbar, #historyMessages::-webkit-scrollbar {
            display: none;
        }

        #msgInput {
            width: 100%;
            padding: 5px;
            margin-bottom: 10px;
        }

        /* 按钮样式 */
        button {
            background-color: #007bff;
            color: #fff;
            border: none;
            border-radius: 3px;
            padding: 5px 10px;
            cursor: pointer;
            margin-right: 5px;
        }

        button:hover {
            background-color: #0056b3;
        }

        button:active {
            background-color: #004494;
        }

        /* 录音和停止按钮的特殊样式 */
        #record, #stop {
            background-color: #28a745;
        }

        #record:hover {
            background-color: #218838;
        }

        #stop {
            background-color: #dc3545;
        }

        #stop:hover {
            background-color: #c82333;
        }

        #error {
            color: red;
            margin-top: 10px;
        }

        .user {
            cursor: pointer;
            color: blue;
        }

        #toggleMessages {
            margin-top: 10px;
        }

        #messages p, #privateMessages p {
            text-align: left;
            margin: 5px 0;
        }

        .message-container {
            margin-bottom: 10px;
        }

        .message-container.user-message {
            text-align: right;
        }

        .message-container.other-message {
            text-align: left;
        }

        .message-content {
            max-width: 70%;
            padding: 5px;
            border-radius: 3px;
            background-color: #f0f0f0;
            display: inline-block;
        }

        .message-content.user-message {
            background-color: #e0f7fa;
        }

        .message-content.other-message {
            background-color: #f0f0f0;
        }

        .message-content audio {
            width: 100%;
        }

        /* 炫彩文字效果 */
        @keyframes neonGlow {
            0% { text-shadow: 0 0 5px #fff, 0 0 10px #fff, 0 0 15px #ff00de, 0 0 20px #ff00de, 0 0 25px #ff00de, 0 0 30px #ff00de, 0 0 35px #ff00de; }
            50% { text-shadow: 0 0 5px #fff, 0 0 10px #fff, 0 0 15px #00ffff, 0 0 20px #00ffff, 0 0 25px #00ffff, 0 0 30px #00ffff, 0 0 35px #00ffff; }
            100% { text-shadow: 0 0 5px #fff, 0 0 10px #fff, 0 0 15px #ff00de, 0 0 20px #ff00de, 0 0 25px #ff00de, 0 0 30px #ff00de, 0 0 35px #ff00de; }
        }

        h1 {
            color: #333;
            font-size: 36px;
            text-align: center;
            animation: none;
        }

    </style>
</head>

<body>
<div class="container">
    <h1>海带聊天室</h1>
    <div class="top">
        <div id="messages"></div>
        <div id="privateMessages">
            <div id="unreadCount" style="display: none; color: red; font-weight: bold;"></div>
        </div>
        <div id="users">
            <span>水军榜</span>
            <ul id="userList"></ul>
        </div>
        <div id="rank">
            <span>水贴王榜</span>
            <ul id="rankList"></ul>
            <button id="updateRank">更新排行榜</button> <!-- 添加更新按钮 -->
        </div>
    </div>
    <button id="toggleMessages">切换到私聊</button>
    <input type="text" id="recipient" placeholder="接收者（留空发送给所有人）"
           style="width: 22%; padding: 5px; margin-bottom: 10px;">
    <input type="file" id="fileInput" style="width: 14%; padding: 5px; margin-bottom: 10px;" accept=".txt,.pdf,.jpg,.png" onchange="checkFileSize(this)">
    <button id="record">录音</button>
    <button id="stop">停止</button>
    <input type="text" id="msgInput" placeholder="输入消息...">
    <button id="send">发送</button>
    <button id="showHistory">查看水贴记录</button>
    <div id="error"></div>
    <!-- 添加 historyMessages 元素 -->
    <div id="historyMessages">
    </div>

</div>
<script>
    // 获取URL参数中的用户名，如果没有则生成一个随机用户名并存储在localStorage中
    const urlParams = new URLSearchParams(window.location.search);
    let username = urlParams.get('user');
    if (!username) {
        username = localStorage.getItem('chatUsername');
        if (!username) {
            username = `用户_${Date.now().toString(36)}`;
            localStorage.setItem('chatUsername', username);
        }
    }
    // 检查用户名长度，超过20字符则截断
    if (username.length > 20) {
        username = username.substring(0, 20);
        localStorage.setItem('chatUsername', username);
    }
    const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
    const ws = new WebSocket(`${protocol}//${window.location.host}/websocket?user=${username}`);
    let mediaRecorder;
    let audioChunks = [];

    // 录音按钮点击事件处理函数
    document.getElementById('record').addEventListener('click', () => {
        navigator.mediaDevices.getUserMedia({ audio: true })
            .then(stream => {
                mediaRecorder = new MediaRecorder(stream);
                mediaRecorder.ondataavailable = event => {
                    audioChunks.push(event.data);
                };
                mediaRecorder.onstop = () => {
                    const audioBlob = new Blob(audioChunks, { type: 'audio/wav' });
                    const reader = new FileReader();
                    reader.onload = function(event) {
                        const base64Audio = event.target.result.split(',')[1];
                        sendVoice(base64Audio, 'audio/wav');
                    };
                    reader.readAsDataURL(audioBlob);
                    audioChunks = [];
                };
                mediaRecorder.start();
            })
            .catch(err => console.error('录音错误:', err));
    });

    // 停止录音按钮点击事件处理函数
    document.getElementById('stop').addEventListener('click', () => {
        if (mediaRecorder && mediaRecorder.state !== 'inactive') {
            mediaRecorder.stop();
        }
    });

    // 发送语音消息函数
    function sendVoice(base64Voice, voiceType) {
        const recipient = document.getElementById('recipient').value || 'all';
        const message = {
            type: 'VOICE',
            user: recipient,
            voice: base64Voice,
            voiceType: voiceType,
            timestamp: new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }), // 修改: 使用中国时区的时间戳
            sender: username // 添加 sender 属性
        };
        if (base64Voice) {
            const voiceFilename = `voice_${Date.now().toString(36)}.wav`;
            message.voiceFilename = voiceFilename;
        }
        ws.send(JSON.stringify(message));
        
        // 立即显示发送者的语音消息
        displayVoiceMessage(message, document.getElementById("messages"), true);
    }

    // WebSocket消息接收处理函数
    ws.onmessage = (event) => {
        const message = JSON.parse(event.data);
        switch (message.type) {
            case 'JOIN':
                if (message.user) {
                    document.getElementById("messages").innerHTML += `<p style="font-size: 20px;  font-weight: bold; color: crimson"><strong>管理员【海带】:</strong> ${message.user} 加入海带聊天室</p>`;
                    updateUsersList([...new Set([...userList.children].map(li => li.textContent), message.user)]);
                }
                break;
            case 'LEAVE':
                if (message.user) {
                    document.getElementById("messages").innerHTML += `<p style="font-size: 20px;  font-weight: bold; color: crimson"><strong>管理员【海带】:</strong> ${message.user} 离开海带聊天室</p>`;
                    updateUsersList([...new Set([...userList.children].map(li => li.textContent)).delete(message.user)]);
                }
                break;
            case 'MESSAGE':
                // 根据消息的接收者和发送者决定消息显示的位置
                if (message.user === 'all') {
                    console.log('Public message:', message); // 添加调试信息
                    displayMessage(message, document.getElementById("messages"), message.sender === username);
                } else if (message.sender === username || message.user === username) {
                    console.log('Private message:', message); // 添加调试信息
                    displayMessage(message, document.getElementById("privateMessages"), message.sender === username);
                }
                break;
            case 'ERROR':
                document.getElementById("error").innerText = message.text;
                setTimeout(() => {
                    document.getElementById("error").innerText = '';
                }, 1000);
                break;
            case 'FILE':
                // 根据消息的接收者和发送者决定文件消息显示的位置
                if (message.user === 'all') {
                    console.log('Public file message:', message); // 添加调试信息
                    displayFileMessage(message, document.getElementById("messages"), message.sender === username);
                } else if (message.sender === username || message.user === username) {
                    console.log('Private file message:', message); // 添加调试信息
                    displayFileMessage(message, document.getElementById("privateMessages"), message.sender === username);
                }
                break;
            case 'USERS':
                updateUsersList(message.users);
                break;
            case 'HISTORY':
                const historyDiv = document.getElementById('historyMessages');
                historyDiv.innerHTML = '';
                message.messages.forEach(msg => {
                    if (msg.type === 'FILE') {
                        displayFileMessage(msg, historyDiv, msg.sender === username);
                    } else if (msg.type === 'VOICE') {
                        displayVoiceMessage(msg, historyDiv, msg.sender === username);
                    } else {
                        displayMessage(msg, historyDiv, msg.sender === username);
                    }
                });
                historyDiv.style.display = 'block';
                // 标记消息为已读
                markMessagesAsRead(message.messages);
                // 更新未读消息数量
                updateUnreadCount(0);
                break;
            case 'VOICE':
                // 根据消息的接收者和发送者决定语音消息显示的位置
                if (message.user === 'all') {
                    displayVoiceMessage(message, document.getElementById("messages"), message.sender === username);
                } else if (message.sender === username || message.user === username) {
                    displayVoiceMessage(message, document.getElementById("privateMessages"), message.sender === username);
                }
                break;
            case 'RANK':
                updateRankList(message.topUsers);
                break;
            case 'UNREAD_COUNT':
                updateUnreadCount(message.count);
                break;
            case 'MARK_READ':
                updateUnreadCount(0);
                break;
            default:
                console.warn("未知消息类型:", message.type);
        }
    };

    function updateUsersList(users) {
        const userList = document.getElementById("userList");
        userList.innerHTML = '';
        users.forEach(user => {
            const li = document.createElement("li");
            li.textContent = user;
            li.onclick = () => {
                document.getElementById('recipient').value = user;
            };
            userList.appendChild(li);
        });
    }

    // 新增方法：更新排行榜列表
    function updateRankList(topUsers) {
        const rankList = document.getElementById("rankList");
        rankList.innerHTML = '';
        topUsers.forEach((user, index) => {
            const li = document.createElement("li");
            li.textContent = `${index + 1}. ${user.username} (${user.messageCount} 条)`; // 显示用户名和消息条数
            rankList.appendChild(li);
        });
    }

    // 文件上传处理函数
    function uploadFile(callback) {
        const fileInput = document.getElementById('fileInput');
        const file = fileInput.files[0];
        if (!file) {
            callback(null, null, null);
            return;
        }
        const reader = new FileReader();
        reader.onload = function(event) {
            const base64File = event.target.result.split(',')[1];
            callback(base64File, file.name, file.type);
        };
        reader.onerror = function(event) {
            console.error('文件读取错误:', event);
            callback(null, null, null);
        };
        reader.readAsDataURL(file);
    }

    // 发送消息函数
    function send() {
        const msgInput = document.getElementById("msgInput").value;
        const recipient = document.getElementById('recipient').value || 'all';

        uploadFile((base64File, filename, fileType) => {
            const messages = [];

            if (msgInput.trim() !== '') {
                messages.push({
                    type: 'MESSAGE',
                    user: recipient,
                    sender: username, // 确保包含用户名
                    message: msgInput,
                    timestamp: new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }) // 修改: 使用中国时区的时间戳
                });
            }

            if (base64File) {
                messages.push({
                    type: 'FILE',
                    user: recipient,
                    sender: username, // 确保包含用户名
                    file: base64File,
                    filename: filename,
                    fileType: fileType,
                    timestamp: new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }) // 修改: 使用中国时区的时间戳
                });
            }

            const base64Voice = localStorage.getItem('base64Voice');
            const voiceType = localStorage.getItem('voiceType');
            if (base64Voice) {
                messages.push({
                    type: 'VOICE',
                    user: recipient,
                    sender: username, // 确保包含用户名
                    voice: base64Voice,
                    voiceType: voiceType,
                    timestamp: new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }) // 修改: 使用中国时区的时间戳
                });
                localStorage.removeItem('base64Voice');
                localStorage.removeItem('voiceType');
            }

            if (messages.length === 0) {
                alert('请输入消息或选择文件');
                return;
            }

            messages.forEach(msg => {
                ws.send(JSON.stringify(msg));
                if (msg.type === 'MESSAGE') {
                    displayMessage(msg, recipient === 'all' ? document.getElementById("messages") : document.getElementById("privateMessages"), msg.sender === username);
                } else if (msg.type === 'FILE') {
                    displayFileMessage(msg, recipient === 'all' ? document.getElementById("messages") : document.getElementById("privateMessages"), msg.sender === username);
                } else if (msg.type === 'VOICE') {
                    displayVoiceMessage(msg, recipient === 'all' ? document.getElementById("messages") : document.getElementById("privateMessages"), msg.sender === username);
                }
            });

            document.getElementById("msgInput").value = '';
            document.getElementById("fileInput").value = '';
        });
    }

    // WebSocket连接成功事件处理函数
    ws.onopen = () => {
        document.getElementById("messages").innerHTML += `<p style="font-size: 20px;  font-weight: bold; color: crimson"><strong>管理员【海带】:</strong> 欢迎 ${username} 加入海带聊天室</p>`;
    };

    // WebSocket连接关闭事件处理函数
    ws.onclose = () => {
        document.getElementById("messages").innerHTML += `<p style="font-size: 20px;  font-weight: bold; color: crimson"><strong>管理员【海带】:</strong> ${username} 已离开海带聊天室</p>`;
    };

    // WebSocket错误事件处理函数
    ws.onerror = (error) => {
        console.error('WebSocket 错误:', error);
        document.getElementById("error").innerText = '发生错误，请刷新页面重试';
    };

    // 开始私聊函数
    function startPrivateChat(user) {
        document.getElementById('recipient').value = user;
    }

    // 切换公共消息和私聊消息显示的函数
    function toggleMessages() {
        const messages = document.getElementById("messages");
        const privateMessages = document.getElementById("privateMessages");
        const toggleButton = document.getElementById("toggleMessages");

        if (messages.style.display === 'none') {
            messages.style.display = 'block';
            privateMessages.style.display = 'none';
            toggleButton.innerText = '切换到私聊';
        } else {
            messages.style.display = 'none';
            privateMessages.style.display = 'block';
            toggleButton.innerText = '切换到公共消息';

            // 发送 MARK_READ 消息给后端
            const recipient = document.getElementById('recipient').value;
            if (recipient) {
                const message = JSON.stringify({ type: 'MARK_READ', user: recipient });
                ws.send(message);
            }
        }
    }

    // 页面加载完成后为按钮添加事件监听器
    document.addEventListener('DOMContentLoaded', function() {
        const sendButton = document.getElementById('send');
        if (sendButton) {
            sendButton.addEventListener('click', send);
        } else {
            console.error('发送按钮元素未找到');
        }

        const showHistoryButton = document.getElementById('showHistory');
        if (showHistoryButton) {
            showHistoryButton.addEventListener('click', showMessageHistory);
        } else {
            console.error('显示历史消息按钮元素未找到');
        }

        const toggleMessagesButton = document.getElementById('toggleMessages');
        if (toggleMessagesButton) {
            toggleMessagesButton.addEventListener('click', toggleMessages);
        } else {
            console.error('切换消息按钮元素未找到');
        }

        const updateRankButton = document.getElementById('updateRank');
        if (updateRankButton) {
            updateRankButton.addEventListener('click', updateRank);
        } else {
            console.error('更新排行榜按钮元素未找到');
        }
    });

    // 显示历史消息函数
    function showMessageHistory() {
        const historyDiv = document.getElementById('historyMessages');
        if (historyDiv.style.display === 'block') {
            historyDiv.style.display = 'none';
        } else {
            const message = JSON.stringify({ type: 'GET_HISTORY' });
            ws.send(message);
            const originalOnMessage = ws.onmessage;
            ws.onmessage = (event) => {
                const message = JSON.parse(event.data);
                if (message.type === 'HISTORY') {
                    historyDiv.innerHTML = '';
                    message.messages.forEach(msg => {
                        if (msg.type === 'FILE') {
                            displayFileMessage(msg, historyDiv, msg.sender === username);
                        } else if (msg.type === 'VOICE') {
                            // 确保 voiceFilename 字段被传递
                            displayVoiceMessage(msg, historyDiv, msg.sender === username);
                        } else {
                            displayMessage(msg, historyDiv, msg.sender === username);
                        }
                    });
                    historyDiv.style.display = 'block';
                    ws.onmessage = originalOnMessage;
                    // 更新未读消息数量
                    updateUnreadCount(0);
                    // 发送 MARK_READ 消息给后端
                    const recipient = document.getElementById('recipient').value;
                    if (recipient) {
                        const markReadMessage = JSON.stringify({ type: 'MARK_READ', user: recipient });
                        ws.send(markReadMessage);
                    }
                } else {
                    originalOnMessage(event);
                }
            };
        }
    }

    // 检查文件大小的函数
    function checkFileSize(input) {
        const file = input.files[0];
        if (file && file.size > 10 * 1024 * 1024) {
            alert('文件大小不能超过 10MB');
            input.value = '';
        }
    }

    function displayMessage(message, container, isSender) {
        const messageContainer = document.createElement('div');
        messageContainer.classList.add('message-container');
        messageContainer.classList.add(isSender ? 'user-message' : 'other-message');

        const messageContent = document.createElement('div');
        messageContent.classList.add('message-content');
        messageContent.classList.add(isSender ? 'user-message' : 'other-message');

        // 使用服务器传递的时间戳
        const localTimestamp = new Date(message.timestamp).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' });

        messageContent.innerHTML = `<strong class="user" onclick="startPrivateChat('${message.sender}')">${message.sender}:</strong> ${message.message} ${message.filename ? `(${message.filename})` : ''} <span style="font-size: 0.8em;">(${localTimestamp})</span>`;
        messageContainer.appendChild(messageContent);
        container.appendChild(messageContainer);
    }

    function displayFileMessage(message, container, isSender) {
        const messageContainer = document.createElement('div');
        messageContainer.classList.add('message-container');
        messageContainer.classList.add(isSender ? 'user-message' : 'other-message');

        const messageContent = document.createElement('div');
        messageContent.classList.add('message-content');
        messageContent.classList.add(isSender ? 'user-message' : 'other-message');

        const a = document.createElement('a');
        a.href = `data:${message.fileType};base64,${message.file}`;
        a.download = message.filename;
        a.textContent = message.filename;

        messageContent.innerHTML = `<strong class="user" onclick="startPrivateChat('${message.sender}')">${message.sender}:</strong> `;
        messageContent.appendChild(a);
        messageContent.innerHTML += ` <span style="font-size: 0.8em;">(${message.timestamp})</span>`; // 删除: 原始代码
        messageContent.innerHTML += ` <span style="font-size: 0.8em;">(${new Date(message.timestamp).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })})</span>`; // 修改: 使用中国时区的时间戳
        messageContainer.appendChild(messageContent);
        container.appendChild(messageContainer);
    }

    function displayVoiceMessage(message, container, isSender) {
        const messageContainer = document.createElement('div');
        messageContainer.classList.add('message-container');
        messageContainer.classList.add(isSender ? 'user-message' : 'other-message');

        const messageContent = document.createElement('div');
        messageContent.classList.add('message-content');
        messageContent.classList.add(isSender ? 'user-message' : 'other-message');

        const audio = document.createElement('audio');
        audio.src = `data:${message.voiceType};base64,${message.voice}`;
        audio.controls = true;

        // 优化播放逻辑
        audio.addEventListener('play', () => {
            if (audio.paused) {
                audio.play().catch(error => {
                    if (error.name !== 'AbortError') {
                        console.error('播放音频时出错:', error);
                    }
                });
            }
        });

        // 添加播放按钮点击事件
        audio.addEventListener('click', (e) => {
            e.preventDefault();
            if (audio.paused) {
                audio.play().catch(error => {
                    if (error.name !== 'AbortError') {
                        console.error('播放音频时出错:', error);
                    }
                });
            } else {
                audio.pause();
            }
        });

        messageContent.innerHTML = `<strong class="user" onclick="startPrivateChat('${message.sender}')">${message.sender}:</strong> `;
        messageContent.appendChild(audio);
        if (message.voiceFilename) {
            messageContent.innerHTML += ` (${message.voiceFilename})`;
        } else {
            messageContent.innerHTML += ` (语音消息)`;
        }
        // 使用服务器传递的时间戳
        const localTimestamp = new Date(message.timestamp).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' });
        messageContent.innerHTML += ` <span style="font-size: 0.8em;">(${localTimestamp})</span>`;
        messageContainer.appendChild(messageContent);
        container.appendChild(messageContainer);
    }

    // 新增方法：标记消息为已读
    function markMessagesAsRead(messages) {
        const messageIds = messages.map(msg => msg.id);
        const message = JSON.stringify({ type: 'MARK_READ', messageIds: messageIds });
        ws.send(message);
    }

    function updateUnreadCount(count) {
        const unreadCountDiv = document.getElementById('unreadCount');
        if (count > 0) {
            unreadCountDiv.innerText = `未读消息: ${count}`;
            unreadCountDiv.style.display = 'block';
        } else {
            unreadCountDiv.style.display = 'none';
        }
    }

    // 更新排行榜函数
    function updateRank() {
        const message = JSON.stringify({ type: 'GET_RANK' });
        ws.send(message);
    }
</script>
</body>

</html>
















